/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package estructurassa.expertos;

import estructurassa.entidades.DetalleOrden;
import estructurassa.entidades.DetallePedidoAProveedores;
import estructurassa.entidades.DetalleProducto;
import estructurassa.entidades.EstadoOrdenDeProduccion;
import estructurassa.entidades.Material;
import estructurassa.entidades.OrdenDeProduccion;
import estructurassa.entidades.OrdenDeProduccionEstado;
import estructurassa.entidades.ProductoTerminado;
import estructurassa.expertos.excepciones.ABMOrdenDeProducicionException;
import estructurassa.expertos.excepciones.ExpertoStockException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import persistencia.Conexion;
import persistencia.Fachada;
import persistencia.exceptions.ExcepcionFachada;

/**
 *
 * @author Juan
 */
public class ExpertoABMOrdenDeProduccion implements Experto {

    ExpertoStock expertoStock;
    ExpertoABMPedidoAProveedores expertoPedidoAProv;

    public ExpertoABMOrdenDeProduccion() {
        expertoStock = (ExpertoStock) FabricaExpertos.getInstancia().getExperto(FabricaExpertos.expertos.STOCK);
        expertoPedidoAProv = (ExpertoABMPedidoAProveedores) FabricaExpertos.getInstancia().getExperto(FabricaExpertos.expertos.ABM_PEDIDO_PROVEEDOR);
    }

    public List buscarProductosTerminados() {
        Criteria criterioProductosVigentes = Fachada.getInstancia().crearCriterio(ProductoTerminado.class);
        criterioProductosVigentes.add(Restrictions.eq("vigente", true));

        List productos = Fachada.getInstancia().buscar(ProductoTerminado.class, criterioProductosVigentes);

        return productos;
    }

    public void entregarOrden(OrdenDeProduccion orden) throws ExcepcionFachada {
        setEstadoOrden(orden, estado.ENTREGADA);
        Fachada.getInstancia().guardar(orden);
    }

    public void guardar(OrdenDeProduccion orden) throws ABMOrdenDeProducicionException {
        if (orden.getId() == null) {
            List ordenes = (Fachada.getInstancia().buscar(OrdenDeProduccion.class, null));
            orden.setNumero(Long.parseLong(ordenes.size() + 1 + ""));
        }
        validarOrden(orden);
        validarExistencia(orden);

        List<DetallePedidoAProveedores> detallesPedido = new ArrayList<>();

        for (DetalleOrden detalleOrden : orden.getListaDetalles()) {
            Float cantidadProductoTerminadoPedidaEnStock = expertoStock.consultarCantidadDisponible(detalleOrden.getProductoTerminado());
            if (detalleOrden.getCantidad() > cantidadProductoTerminadoPedidaEnStock) {
                Float cantidadFaltanteDeProductoTerminado = detalleOrden.getCantidad() - cantidadProductoTerminadoPedidaEnStock;
                //aca tengo la cantidad de productos que me faltan para cumplir el pedido

                ProductoTerminado productoterminado = detalleOrden.getProductoTerminado();

                for (DetalleProducto detalleProducto : productoterminado.getDetalles()) {
                    if (detalleProducto.getPieza() instanceof Material) {

                        //Ya que no hay productos terminados, me fijo ahora material por material si hay stock
                        Float cantidadMateriaPrimaEnStock = expertoStock.consultarCantidadDisponible((Material) detalleProducto.getPieza());

                        if (detalleProducto.getCantidad() * cantidadFaltanteDeProductoTerminado > cantidadMateriaPrimaEnStock) {
                            DetallePedidoAProveedores detalle = new DetallePedidoAProveedores();
                            detalle.setCantidad(detalleProducto.getCantidad() * cantidadFaltanteDeProductoTerminado - cantidadMateriaPrimaEnStock);
                            detalle.setMaterial((Material) detalleProducto.getPieza());
                            detallesPedido.add(detalle);
                        }
                    }

                }

            }
        }
        if (!detallesPedido.isEmpty()) {
            setEstadoOrden(orden, estado.ESPERA);
        } else {
            setEstadoOrden(orden, estado.PRODUCCION);
        }

        try {
            Conexion.getInstancia().iniciarTX();
            Fachada.getInstancia().guardar(orden);
            if (!detallesPedido.isEmpty()) {
                expertoPedidoAProv.armarPedidoProveedorPredeterminadoSinTX(detallesPedido, orden);
                // if orden.getEstado().getEstado().equalsIgnoreCase("EN ESPERA")
                // expertoStock.crearReserva(orden);
                //

            }
            Conexion.getInstancia().confirmarTx();
        } catch (ExcepcionFachada ex) {
            Conexion.getInstancia().deshacerTx();
            Logger.getLogger(ExpertoABMOrdenDeProduccion.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    public List buscarOrdenes() {
        return Fachada.getInstancia().buscar(OrdenDeProduccion.class, null);
    }

    public List buscarOrdenes(String numero) {
        Criteria criterioNombre = Fachada.getInstancia().crearCriterio(OrdenDeProduccion.class);
        criterioNombre.add(Restrictions.ilike("numero", numero));
        return Fachada.getInstancia().buscar(OrdenDeProduccion.class, criterioNombre);
    }

    public List buscarOrdenesEnProduccion() {
        Criteria criterioEstado = Fachada.getInstancia().crearCriterio(OrdenDeProduccion.class);
        EstadoOrdenDeProduccion estadoProduccion = getEstadoOrden(estado.PRODUCCION);
        criterioEstado.createCriteria("estados").add(Restrictions.eq("estado", estadoProduccion));

        return Fachada.getInstancia().buscar(OrdenDeProduccion.class, criterioEstado);
    }

    public List buscarOrdenesEnEspera() {
        Criteria criterioEstado = Fachada.getInstancia().crearCriterio(OrdenDeProduccion.class);
        EstadoOrdenDeProduccion estadoProduccion = getEstadoOrden(estado.ESPERA);
        criterioEstado.createCriteria("estados").add(Restrictions.eq("estado", estadoProduccion));

        return Fachada.getInstancia().buscar(OrdenDeProduccion.class, criterioEstado);

    }

    public List buscarOrdenesEntregadas() {
        Criteria criterioEstado = Fachada.getInstancia().crearCriterio(OrdenDeProduccion.class);
        EstadoOrdenDeProduccion estadoProduccion = getEstadoOrden(estado.ENTREGADA);
        criterioEstado.createCriteria("estados").add(Restrictions.eq("estado", estadoProduccion));

        return Fachada.getInstancia().buscar(OrdenDeProduccion.class, criterioEstado);
    }

    private void validarOrden(OrdenDeProduccion orden) throws ABMOrdenDeProducicionException {

        if (orden.getListaDetalles() == null || orden.getListaDetalles().isEmpty()) {
            throw new ABMOrdenDeProducicionException("No se han cargado correctamente los detalles de la orden");
        }

    }

    private void validarExistencia(OrdenDeProduccion orden) throws ABMOrdenDeProducicionException {
        if (orden.getId() != null) {
            return;
        }
        Criteria numeroOrden = Fachada.getInstancia().crearCriterio(OrdenDeProduccion.class);
        numeroOrden.add(Restrictions.eq("numero", orden.getNumero()));
        if (!Fachada.getInstancia().buscar(OrdenDeProduccion.class, numeroOrden).isEmpty()) {
            throw new ABMOrdenDeProducicionException("Orden ya existente");
        }
    }

    public void guardarModificacion(OrdenDeProduccion orden) {
        try {
            Fachada.getInstancia().guardar(orden);
        } catch (ExcepcionFachada ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Inicia la producción de una orden, ¡¡¡¡NO USA TRANSACCIONES!!!!
     *
     * @param orden
     */
    public void iniciarProduccionOrdenSinTX(OrdenDeProduccion orden) throws ABMOrdenDeProducicionException {
        setEstadoOrden(orden, estado.PRODUCCION);
        //Consumir materiales orden
        try {
            expertoStock.darDeBajaStockDeOrdenSinTX(orden);
        } catch (ExpertoStockException ex) {
            throw new ABMOrdenDeProducicionException("Error al dar de baja stock de orden");
        }
        try {
            Fachada.getInstancia().guardar(orden);
        } catch (ExcepcionFachada ex) {
            throw new ABMOrdenDeProducicionException("Error al dar de baja stock de orden");
        }
    }

    private enum estado {

        ESPERA, PRODUCCION, ENTREGADA
    }

    private EstadoOrdenDeProduccion getEstadoOrden(estado opcion) {
        String nombreEstado = "";
        switch (opcion) {
            case ESPERA:
                nombreEstado = "En espera";
                break;
            case PRODUCCION:
                nombreEstado = "En Produccion";
                break;
            case ENTREGADA:
                nombreEstado = "Entregada";
        }
        Criteria criterioEstado = Fachada.getInstancia().crearCriterio(EstadoOrdenDeProduccion.class);
        criterioEstado.add(Restrictions.ilike("estado", nombreEstado));
        EstadoOrdenDeProduccion estadoOrden = (EstadoOrdenDeProduccion) Fachada.getInstancia().buscar(EstadoOrdenDeProduccion.class, criterioEstado).get(0);
        return estadoOrden;
    }

    private void setEstadoOrden(OrdenDeProduccion orden, estado opcion) {
        for (OrdenDeProduccionEstado ordenDeProduccionEstado : orden.getEstados()) {
            ordenDeProduccionEstado.setEstadoActual(false);
        }
        EstadoOrdenDeProduccion estadoOrden = getEstadoOrden(opcion);
        OrdenDeProduccionEstado nuevoEstado = new OrdenDeProduccionEstado();
        nuevoEstado.setEstado(estadoOrden);
        nuevoEstado.setEstadoActual(true);
        nuevoEstado.setFecha(Fachada.getInstancia().getFecha());
        orden.getEstados().add(nuevoEstado);
    }
}
